<?php

namespace App\Modules\LuckDraw\Strategy;

use \App\Modules\LuckDraw\Award\AwardFactory;
use App\Modules\LuckDraw\Award\AwardInterface;
use App\Modules\LuckDraw\Models\AwardPoolModel;
use Illuminate\Support\Facades\Redis;

class PersonalStrategy implements StrategyInterface {
    private $_name = "personal";
    private $_luckId;
    private $_userId;
    private $_ext;
    private $_pool;
    private $_awardRedisKey;

    public function __construct(int $luckId, int $userId, ?object $ext,  object $pool){
        $this->_luckId = $luckId;
        $this->_userId = $userId;
        $this->_ext = $ext;
        $this->_pool = $pool;
        $this->_awardRedisKey =
            sprintf('{LUCKDRAW:STRATEGY:PERSONAL:DRAW:%s:POOL:%s:%s}', $luckId, $pool->id, $userId);
    }

    public function pool():?object{
        return $this->_pool;
    }

    public function poolId():?int{
        return $this->pool()->id;
    }

    public function name():?string{
        return $this->_name;
    }

    public function run(int $num) : ?array {
        if ($num <= 0) {
            return [];
        }
        //随机取出$num个奖品
        $awardArr = [];
        for ($i = 0; $i < $num; $i++) {
            //从集合里随机拿一个奖品
            $award = $this->_pop();
            //为空则奖品池没有初始化
            if ($award == null) {
                //开始初始化
                $ok = $this->_initAwardPool();
                //如果初始化失败返回null
                if (!$ok) {
                    return null;
                }
                $award = $this->_pop();
            }

            if ($award == null) {
                return null;
            }
            $k = $award[0] . '-' . $award[1] . '-' . $award[2];
            if (isset($awardArr[$k])) {
                $awardArr[$k]->num++; //数量加
                continue;
            }
            //转化成对象
            $award =  AwardFactory::get(
                $this->_luckId,
                $this->_userId,
                $award[0],
                $award[1],
                $award[2],
                $award[3]
            );
            $awardArr[$k] = $award;
        }

        //成功返回奖品列表
        $retAwards = array_values($awardArr);
        return array_values($retAwards);
    }

    private function  _pop() : ?array {
        $luaStr = "local funname = 'spop'
                KEYS[5] = KEYS[1] .. ':0'
                KEYS[6] = KEYS[1] .. ':1'
                KEYS[7] = KEYS[1] .. ':POOLNAME'
                local tmpA = redis.call('get', KEYS[7]);
                if tmpA then
                    KEYS[5] = tmpA
                end
                if  KEYS[5] == KEYS[1] .. ':1' then
                    KEYS[6] = KEYS[1] .. ':0'
                end
                local val = redis.call('spop',KEYS[5])
                if val then
                    redis.call('sadd', KEYS[6], val)
                    return val
                else
                    val = redis.call('spop',KEYS[6])
                    if val then
                        redis.call('sadd', KEYS[5], val)
                        redis.call('set', KEYS[7], KEYS[6])
                        return val
                    end
                end
                return nil
                ";
        $val = Redis::EVAL($luaStr, 1, $this->_awardRedisKey);
        if ($val == null) {
            return null;
        }
        list($index, $type, $awardId, $timeLimit, $price) = explode('-', $val);

        return [$type, $awardId, $timeLimit, $price];
    }

    //初始化奖品数据
    private function _initAwardPool () : bool {
        //已经存在了就不需要再初始化了
        if (Redis::EXISTS($this->_awardRedisKey . ":0") || Redis::EXISTS($this->_awardRedisKey . ":1")) {
            return false;
        }

        //拿出奖池数据
        $poolData =  $this->_getPoolData();

        $lock = redis_lock($this->_awardRedisKey . ":0");
        if (!$lock) {
            return false;
        }
        //将奖品数据放入redis 集合
        $res = Redis::Pipeline(function($pipe) use($poolData) {
            $pipe->set($this->_awardRedisKey . ":POOLNAME", $this->_awardRedisKey . ":0");
            $index = 1;
            foreach ($poolData as $award) {
                for ($i = 0; $i < $award->num; $i++) {
                    $pipe->sadd($this->_awardRedisKey . ":0", $index++ . '-' . $award->type . '-' . $award->id . '-' . $award->time . '-' . $award->price);
                }
            }
        });
        redis_unlock($this->_awardRedisKey . ":0");
        return true;
    }

    //根据奖池id，获取奖池数据
    function _getPoolData(): ?array{
        return json_decode($this->pool()->pool);
    }

    //获取奖池数据
    function getPoolData(): ?array{
        return json_decode($this->pool()->pool);
    }

    //抽奖失败的回滚
    public function rollBack() {
        // TODO: Implement rollBack() method.
    }

    public function delPoolRedis():?bool {
        write_log('ActivityPool','清除redis',$this->_awardRedisKey);
        Redis::del($this->_awardRedisKey.':0');
        Redis::del($this->_awardRedisKey.':1');
        Redis::del($this->_awardRedisKey.':POOLNAME');
        return true;
    }

    public function upAwardStrategy(array $poolDatas):?array{
        return [];
    }

    public static function clearCacheByPoolId(int $luckId, array $userList, int $poolId): bool {
        //清除所有缓存

        $res = Redis::Pipeline(function($pipe) use($luckId, $userList, $poolId) {
            foreach ($userList as $uid) {
                $awardRedisKey = sprintf('{LUCKDRAW:STRATEGY:PERSONAL:DRAW:%s:POOL:%s:%s}', $luckId, $poolId, $uid);
                $pipe->del($awardRedisKey . ":POOLNAME");
                $pipe->del($awardRedisKey . ':0');
                $pipe->del($awardRedisKey . ':1');
            }
        });
        return true;
    }
}
